package com.echat.serviceapigateway.security.service.impl;

import com.echat.commonlib.constant.ErrorCode;
import com.echat.commonlib.constant.ErrorCodeMsg;
import com.echat.commonlib.exception.exc.EchatSecurityException;
import com.echat.commonlib.exception.exc.ValidateFailedException;
import com.echat.serviceapigateway.security.cache.RedisTokenCacher;
import com.echat.serviceapigateway.security.constant.Constants;
import com.echat.serviceapigateway.security.mapper.TokenGetLimitMapper;
import com.echat.serviceapigateway.security.mapper.TokenMapper;
import com.echat.serviceapigateway.security.model.authentication.Token;
import com.echat.serviceapigateway.security.model.authentication.User;
import com.echat.serviceapigateway.security.service.TokenHistoryService;
import com.echat.serviceapigateway.security.service.TokenService;
import com.echat.serviceapigateway.security.service.UserService;
import com.echat.serviceapigateway.security.tool.EncryptUtils;
import com.echat.serviceapigateway.security.tool.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;

/**
 * Created by chenmohan on 2018/08/30
 *
 * @description
 */
@Service
public class TokenServiceImpl implements TokenService {

    @Autowired
    TokenMapper tokenMapper;

    @Autowired
    TokenGetLimitMapper tokenGetLimitMapper;

    @Autowired
    TokenHistoryService tokenHistoryService;

    @Autowired
    UserService userService;

    @Autowired
    RedisTokenCacher tokenCacher;

    /**
     * 使用 UserId、accessKey、accessSecrect获取新的token
     *
     * @param accessKey    用户accessKey
     * @param accessSecret 用户accessSecret
     * @return
     */
    @Override
    public Token accquireAccessToken(String accessKey, String accessSecret) {
        //检查非空
        if (StringUtils.isEmpty(accessKey)) {
            throw new ValidateFailedException(ErrorCode.User.USERNAME_NOT_NULL, "field[accessKey] must not be null or empty");
        }
        if (StringUtils.isEmpty(accessSecret)) {
            throw new ValidateFailedException(ErrorCode.User.USERNAME_NOT_NULL, "field[accessSecret] must not be null or empty");
        }

        //检查accessKey和accessSecrect是否属于该User
        User queryUser = User.builder()
                .accessKey(accessKey)
                .accessSecret(accessSecret).build();
        User resultUser = userService.getUser(queryUser);
        if (resultUser == null) {
            throw new EchatSecurityException(ErrorCode.INVAILD_ACCESS_KEY, ErrorCodeMsg.INVAILD_ACCESS_SECRET);
        }

        //刷新token
        Long userId = resultUser.getId();
        Token accessToken = InsertOrUpdateAccessToken(userId, accessKey, getAccessToken(userId));
        return accessToken;
    }

    /**
     * 使用userId和refreshToken来获取新的token
     *
     * @param accessToken  客户端用户id
     * @param refreshToken 刷新token
     * @return
     */
    @Override
    public Token refreshAccessToken(String accessToken, String refreshToken) {
        if (StringUtils.isEmpty(accessToken)) {
            throw new ValidateFailedException(ErrorCode.User.USERNAME_NOT_NULL, "[accessToken] must not be null");
        }
        if (StringUtils.isEmpty(refreshToken)) {
            throw new ValidateFailedException(ErrorCode.User.USERNAME_NOT_NULL, "[refreshToken] must not be null or empty");
        }
        User queryUser = User.builder().accessKey(accessToken).build();
        User resultUser = userService.getUser(queryUser);
        if (resultUser == null) {
            throw new EchatSecurityException(ErrorCode.INVAILD_ACCESS_KEY, ErrorCodeMsg.INVAILD_ACCESS_KEY);
        }
        Token queryToken = Token.builder().refreshToken(refreshToken).build();
        Token resultToken = tokenMapper.selectOne(queryToken);
        if (resultToken == null) {
            throw new EchatSecurityException(ErrorCode.INVAILD_REFRESH_TOKEN, ErrorCodeMsg.INVAILD_REFRESH_TOKEN);
        }
        return InsertOrUpdateAccessToken(resultUser.getId(), resultUser.getAccessKey(), resultToken);
    }

    /**
     * 通过userId查询token
     *
     * @param userId
     * @return
     */
    @Override
    public Token getAccessToken(Long userId) {
        Token queryToken = Token.builder().userId(userId).build();
        Token resultToken = tokenMapper.selectOne(queryToken);
        return resultToken;
    }

    @Override
    public Token getAccessToken(Token token) {
        token.checkUserIdNotNull();
        tokenMapper.selectOne(token);
        return null;
    }

    @Transactional
    @Override
    public Token InsertOrUpdateAccessToken(Long userId, String accessKey, Token token) {
        LocalDateTime nowTime = LocalDateTime.now();
        if (token == null) {
            //插入
            token = Token.builder()
                    .userId(userId)
                    .accessToken(EncryptUtils.token())
                    .refreshToken(EncryptUtils.token())
                    .accessTokenExpire(nowTime.plusSeconds(Constants.ACCESS_TOKEN_VALID_TIME))
                    .refreshTokenExpire(nowTime.plusSeconds(Constants.REFRESH_TOKEN_VALID_TIME))
                    .createTime(nowTime)
                    .modifyTime(nowTime)
                    .build();
            tokenMapper.insertSelective(token);
        } else {
            //更新
            //如果accessToken没有过期就保存前一个token保存30分钟
            if (token.getAccessTokenExpire().isAfter(nowTime)) {
                String oldAccessToken = token.getAccessToken();
                tokenCacher.cacheSecondToken(accessKey, oldAccessToken);
            }
            //刷新accessToken
            token.setAccessToken(EncryptUtils.token());
            token.setAccessTokenExpire(nowTime.plusSeconds(Constants.ACCESS_TOKEN_VALID_TIME));
            token.setGetTimes(token.getGetTimes() + 1);
            token.setModifyTime(nowTime);
            //如果refreshToken过期了同时也刷新refreshToken
            if (token.getRefreshTokenExpire().isBefore(nowTime)) {   //如果refreshToken已过期，那么刷新refreshToken
                token.setRefreshToken(EncryptUtils.token());
                token.setRefreshTokenExpire(nowTime.plusSeconds(Constants.REFRESH_TOKEN_VALID_TIME));
            }
            tokenMapper.updateByPrimaryKeySelective(token);
            //添加一条token历史使用记录
            tokenHistoryService.fileTokenHistory(token);
        }
        tokenCacher.cacheFirstToken(accessKey, token.getAccessToken());
        return token;

    }

    @Override
    public Long getTTL(String accessToken) {
        if (StringUtils.isEmpty(accessToken)) {
            return -1L;
        }
        return tokenCacher.getExpireTime(accessToken);
    }

}
